﻿using System;
using System.Threading;
using System.Windows.Threading;
using NUnit.Framework;
using Present.Commands.Async;
using Present.Commands.Async.Actions;
using Rhino.Mocks;

namespace Present.Commands.Tests
{
    [TestFixture]
    public class AsyncCommandTestCase : TestCase
    {

        public override void Setup()
        {
            base.Setup();
            target = MockRepository.GenerateStub<IAsyncAction>();
            guiThread = Thread.CurrentThread;
            EnableAsyncCalls();
        }


        private Thread guiThread;
        private IAsyncAction target;

        private void Execute()
        {
            target.Stub(x => x.PrepareWork(null)).Return(new ActionExecution(null));
            var command = new AsyncCommand(target);
            command.Execute(null);
            //Thread.Sleep(100);
        }


        [Test]
        public void ErrorsShouldBeHandledOnGuiThread()
        {
            target
                .Stub(x => x.DoWork(null))
                .Throw(new Exception());

            target.Stub(x => x.HandleError(null))
                .IgnoreArguments()
                .WhenCalled(i => Assert.AreEqual(guiThread, Thread.CurrentThread));

            Execute();
        }

        [Test]
        public void SuccessShouldBeHandledOnGuiThread()
        {
            try
            {

            SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
            bool wasCalledOnGuiThread = false;

            target.Stub(x => x.HandleSuccess(null))
                .IgnoreArguments()
                .WhenCalled(i => wasCalledOnGuiThread = guiThread == Thread.CurrentThread);

            Execute();
            DoEvents();
            Assert.IsTrue(wasCalledOnGuiThread);

            }
            finally
            {
                SynchronizationContext.SetSynchronizationContext(null);
            }
        }

        [Test]
        public void ActionPrepareWorkIsCalled()
        {
            Execute();
            target.AssertWasCalled(x => x.PrepareWork(null));
        }

        [Test]
        public void ActionPrepareIsCalledOnUiThread()
        {
            target
                 .Stub(x => x.PrepareWork(null))
                 .Return(new ActionExecution(null))
                 .WhenCalled(i => Assert.AreSame(guiThread, Thread.CurrentThread));

            target.Stub(x => x.HandleError(null))
                .IgnoreArguments()
                .WhenCalled(i => Assert.Fail("Was not called on gui thread", ((Exception)i.Arguments[0]).Message));

            Execute();
        }

        [Test]
        public void WorkShouldBeDoneOnBackgroundThread()
        {
            target
                .Stub(x => x.DoWork(null))
                .WhenCalled(i => Assert.AreNotEqual(guiThread, Thread.CurrentThread));

            target.Stub(x => x.HandleError(null))
                .IgnoreArguments()
                .WhenCalled(i => Assert.Fail("Was not called on gui thread", ((Exception) i.Arguments[0]).Message));

            Execute();
        }

        [Test]
        public void CanExecuteChangedLinkedToAction()
        {
            bool fired = false;
            var command = new AsyncCommand(target);
            command.CanExecuteChanged += (sender, e) => fired = true;
            target.Raise(x => x.CanExecuteChanged += null, target, EventArgs.Empty);
            Assert.IsTrue(fired);

        }
    }


}

